package com.shanjupay.merchant.service;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shanjupay.common.domain.BusinessException;
import com.shanjupay.common.domain.CommonErrorCode;
import com.shanjupay.common.domain.PageVO;
import com.shanjupay.common.util.PhoneUtil;
import com.shanjupay.merchant.api.MerchantService;
import com.shanjupay.merchant.api.dto.MerchantDTO;
import com.shanjupay.merchant.api.dto.StaffDTO;
import com.shanjupay.merchant.api.dto.StoreDTO;
import com.shanjupay.merchant.convert.MerchantCovert;
import com.shanjupay.merchant.convert.StaffConvert;
import com.shanjupay.merchant.convert.StoreConvert;
import com.shanjupay.merchant.entity.Merchant;
import com.shanjupay.merchant.entity.Staff;
import com.shanjupay.merchant.entity.Store;
import com.shanjupay.merchant.entity.StoreStaff;
import com.shanjupay.merchant.mapper.MerchantMapper;
import com.shanjupay.merchant.mapper.StaffMapper;
import com.shanjupay.merchant.mapper.StoreMapper;
import com.shanjupay.merchant.mapper.StoreStaffMapper;
import com.shanjupay.user.api.TenantService;
import com.shanjupay.user.api.dto.tenant.CreateTenantRequestDTO;
import com.shanjupay.user.api.dto.tenant.TenantDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

//暴露dubbo接口供其他服务进行调用
@Service
@Slf4j
public class MerchantServiceImpl implements MerchantService {
    @Autowired
    MerchantMapper merchantMapper;

    @Autowired
    StoreMapper storeMapper;
    @Autowired
    StaffMapper staffMapper;

    @Autowired
    StoreStaffMapper storeStaffMapper;
    //注入租户服务
    @Reference
    TenantService tenantService;
    /*
    *
    * 根据id进行查询商户信息
    * */
    @Override
    public MerchantDTO queryMerchantById(Long merchantId) {
        //1使用Mapper接口进行查询（Mapper与数据库进行交互）
        Merchant merchant = merchantMapper.selectById(merchantId);
        //使用Mapper接口进行查询
        //属性copy等下使用
//        MerchantDTO merchantDTO = new MerchantDTO();
//        merchantDTO.setId(merchant.getId());
//        merchantDTO.setMerchantName(merchant.getMerchantName());
        /*...........*/
        return  MerchantCovert.INSTANCE.entityToDto(merchant);
    }


    /*
    * 根据租户id进行查询对应的商户
    *
    * */
    @Override
    public MerchantDTO queryMerchantByTenantId(Long tenantId) throws BusinessException{
        //根据租户id 进行查询商户
        Merchant merchant = merchantMapper.selectOne(new LambdaQueryWrapper<Merchant>().eq(Merchant::getTenantId, tenantId));
        return MerchantCovert.INSTANCE.entityToDto(merchant);
    }
/*
* 分页查询当前商户下的门店
*
*
*
* */
    @Override
    public PageVO<StoreDTO> queryStoreByPage(StoreDTO storeDTO, Integer pageNo, Integer pageSize) throws BusinessException {
        //1创建分页条件
        Page<Store> page = new Page<>(pageNo, pageSize);
        //创建设置条件
        LambdaQueryWrapper<Store> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //进行判断设置条件 如果storeDTO 门店存在，并且该门店所诉的商户 进行设置门店所属商户
        if(storeDTO != null && storeDTO.getMerchantId() != null){
            lambdaQueryWrapper.eq(Store::getMerchantId,storeDTO.getMerchantId());
        }
        //进行条件的判断，如果storeDTO 存在，且有所属的门店名称设置
        if(storeDTO != null && !StringUtils.isBlank(storeDTO.getStoreName())){
            lambdaQueryWrapper.eq(Store::getStoreName,storeDTO.getStoreName());
        }

        //进行设置门店
        IPage<Store> storeIPage = storeMapper.selectPage(page, lambdaQueryWrapper);
        //进行获取分页列表
        List<Store> records = storeIPage.getRecords();
        //使用storeCovert进行list<Store>进行转化成list<StoreDTO>
        List<StoreDTO> storeDTOS = StoreConvert.INSTANCE.listentity2dto(records);
        return new PageVO(storeDTOS,storeIPage.getTotal(),pageNo,pageSize);
    }

    /**
     *
     * @param storeId 门店id
     * @param merchantId 商户id
     * @return 该门店和商户是否关联
     */
    @Override
    public Boolean queryStoreInMerchant(Long storeId, Long merchantId) {
        //根据联合查询 store表中是否有这个记录
        Integer count = storeMapper.selectCount(new LambdaQueryWrapper<Store>()
                .eq(Store::getMerchantId, merchantId).eq(Store::getId, storeId));

        return count > 0;
    }

    /*
    * 注册商户
    * @param: 注册商户的信息
    * @return: 注册成功的商户信息
    * 为了程序的可扩展性使用merchantDTO进行接收注册商户的信息
    * */
    @Override
    public MerchantDTO createMerchantDTO(MerchantDTO merchantDTO) {
        //1校验注册的用户信息MerchantDTO对象不为空
        if(merchantDTO == null){
            throw new BusinessException(CommonErrorCode.E_300009);
        }
        //手机号非空校验
        if(StringUtils.isBlank(merchantDTO.getMobile())){
            throw new BusinessException(CommonErrorCode.E_100112);
        }
        //3手机号合法性校验
        if(!PhoneUtil.isMatches(merchantDTO.getMobile())){
            throw  new BusinessException(CommonErrorCode.E_100109);
        }

        if(StringUtils.isBlank(merchantDTO.getPassword())){
            throw new BusinessException(CommonErrorCode.E_100111);
        }
        //4手机号唯一性校验
        //4.1先从数据库根据手机号条件进行查询，是否查询到超过1条记录
        Integer count = merchantMapper.selectCount(new LambdaQueryWrapper<Merchant>().eq(Merchant::getMobile, merchantDTO.getMobile()));
        if(count > 0){
            throw  new BusinessException(CommonErrorCode.E_100113);
        }

         //调用Sass系统接口实现新增租户，用户 绑定住户和用户的关系，赋予用户的的权限，便于操作系统的一些功能
         //1使用Sass系统提供的创建租户和用户的方法 方法，传入住户和用户的信息就可以创建租户和用户绑定租户和用户的关系
         //TenantDTO createTenantAndAccount(CreateTenantRequestDTO createTenantRequest);
         //传入的参数为Sass系统提供CreateTenantRequestDTO 在这个dto设置如下的租户和用户的信息
        /*
        *
        *1、手机号 2、账号 3、密码 4、租户类型：shanju-merchant 5、默认套餐：shanju-merchant 6、租户名称，同账号名
        * */
        //1创建该dto对象
        CreateTenantRequestDTO createTenantRequestDTO = new CreateTenantRequestDTO();
        //2设置租户信息
        createTenantRequestDTO.setMobile(merchantDTO.getMobile());
        createTenantRequestDTO.setUsername(merchantDTO.getUsername());
        createTenantRequestDTO.setPassword(merchantDTO.getPassword());
        createTenantRequestDTO.setTenantTypeCode("shanju-merchant"); //租户类型
        createTenantRequestDTO.setBundleCode("shanju-merchant");    //根据套餐进行分配权限
        createTenantRequestDTO.setName(merchantDTO.getUsername());  //设置于账号同名，

        //调用Sass系统提供的创建租户和用户的api
        //如果租户在Sass中已经存在 Sass直接进行返回
        TenantDTO tenantAndAccount = tenantService.createTenantAndAccount(createTenantRequestDTO);

        //获取租户id, 进行校验判断 租户是否存在
        if(tenantAndAccount == null ||tenantAndAccount.getId() == null  ){
            throw new BusinessException(CommonErrorCode.E_200012);
        }
        Long tenantId = tenantAndAccount.getId();

        //保证租户id在商户表唯一
        Integer count1 = merchantMapper.selectCount(new LambdaQueryWrapper<Merchant>()
                .eq(Merchant::getTenantId, tenantId));
        if(count1 > 0 ){
            throw  new BusinessException(CommonErrorCode.E_200017);
        }
        //创建merchant实体对象 主要是：mapper和数据库进行交互
        //Merchant merchant = new Merchant();
        //注册 手机号添加数据库中
        //merchant.setMobile(merchantDTO.getMobile());
        //1使用对象转化技术，进行dto转化成entity实体对象
        Merchant merchant = MerchantCovert.INSTANCE.dtoToEntity(merchantDTO);
        //sass系统的租户和应用的商户具有一一对应关系，在商户里面添加租户id,表示该商户和sass系统租户对应
        merchant.setTenantId(tenantId);

        //2注册时，还未申请商户 设置申请状态为0
        merchant.setAuditStatus("0");
        merchantMapper.insert(merchant);

        //在应用进行新增门店
        //创建门店storeDTO对象
        StoreDTO storeDTO = new StoreDTO();
        //设置门店的基本信息
        storeDTO.setStoreName("根门店");
        storeDTO.setMerchantId(merchantDTO.getId());
        storeDTO.setStoreStatus(true);
        StoreDTO store = createStore(storeDTO);

        //新增员工 设置员工的手机号 账号 员工所属的门店id  员工所属的商户id
        StaffDTO staffDTO = new StaffDTO();
        staffDTO.setMobile(merchantDTO.getMobile());
        staffDTO.setUsername(merchantDTO.getUsername());
        staffDTO.setStoreId(store.getId());
        staffDTO.setMerchantId(merchantDTO.getId());
        StaffDTO staff = createStaff(staffDTO);
        //在门店绑定员工 设置权限
        bindStaffToStore(store.getId(),staff.getId());


        //3返回注册用户信息，注册成功时，MP自动生成id 把id返回回去
        //merchantDTO.setId(merchant.getId());
        //对象转化技术，把entity实体对象进行转化成entity
         return MerchantCovert.INSTANCE.entityToDto(merchant);

    }

    /**
     * 资质申请接口
     *
     * @param merchantId  商户id
     * @param merchantDTO 资质申请的信息
     * @throws BusinessException
     */
    @Override
    @Transactional
    public void applyMerchant(Long merchantId, MerchantDTO merchantDTO) throws BusinessException {
        //资质申请之前，先进行参数校验
        //1传入参数不能为空 为空抛出自定义 输入参数为空异常
        if(merchantId == null || merchantDTO == null){
            throw  new BusinessException(CommonErrorCode.E_300009);
        }
        //2校验根据id进行查询商户不能为空  ，抛出商户不存在异常
        Merchant merchant = merchantMapper.selectById(merchantId);
        if(merchant == null){
            throw new BusinessException(CommonErrorCode.E_200002);
        }

        //使用map_strup技术，把DTO转化为entity实体 把entity实体保存到数据库中
        Merchant entity = MerchantCovert.INSTANCE.dtoToEntity(merchantDTO);
        //保存到数据库中的entity实体设置必要参数，该参数必须与原有数据库中的商户记录字段保持一致
        entity.setAuditStatus("1"); //提交未审核
        entity.setId(merchantId);
        entity.setMobile(merchant.getMobile());
        entity.setTenantId(merchant.getTenantId()); //用于sass平台接入时 租户id不可变 必须与原有数据库商户 租户保持一致
        //跟新
        merchantMapper.updateById(entity);
    }

    /*
    * 新增门店
    * @param storeDTO 新增的门店
    * @return StoreDTO 返回的新增成功的门店
    * */
    @Override
    public StoreDTO createStore(StoreDTO storeDTO) throws BusinessException {
        //1使用接口转换的storeDTO 转化成store实体
        Store store = StoreConvert.INSTANCE.dto2entity(storeDTO);
        //输出日志
        log.info("新增门店:{}", JSON.toJSON(store).toString());
        //把门店实体entity插入到门店表
        storeMapper.insert(store);
        //3把插入成功门店entity 使用接口转换器，进行转化成dto 并进行返回
        return  StoreConvert.INSTANCE.entity2dto(store);

    }

    /*
    * 新增员工
    * @param:新增员工
    * @return 返回新增成功的员工
    *
    * */
    @Override
    public StaffDTO createStaff(StaffDTO staffDTO) throws BusinessException {
        //参数合法性校验 StaffDTO 员工手机号 员工的用户名 员工的门店id
        if(staffDTO == null || StringUtils.isBlank(staffDTO.getMobile())
        || StringUtils.isBlank(staffDTO.getUsername()) || staffDTO.getStoreId() == null){

            throw new BusinessException(CommonErrorCode.E_300009);
        }

        //门店下的员工的账号和手机号唯一性校验
        Boolean existStaffByMobile = isExistStaffByMobile(staffDTO.getMobile(), staffDTO.getMerchantId());
        if(existStaffByMobile){
            throw new  BusinessException(CommonErrorCode.E_100113);
        }
        Boolean existStaffByUsername = isExistStaffByUsername(staffDTO.getUsername(), staffDTO.getMerchantId());
        if(existStaffByUsername){
            throw new BusinessException(CommonErrorCode.E_100114);
        }
        //新增员工
        //dto转化实体， 员工实体保存到数据库中
        Staff staff = StaffConvert.INSTANCE.dto2entity(staffDTO);
        staffMapper.insert(staff);
        //返回新增成功的员工信息
        return StaffConvert.INSTANCE.entity2dto(staff);
    }

    /*
    * 设置门店管理员为该员工
    * @param: storeId 门店id
    * @param: staffId 员工id
    * 管理员设置失败直接抛出异常 无返回值
    * */
    @Override
    public void bindStaffToStore(Long storeId, Long staffId) throws BusinessException {
        //创建关联实体对象
        StoreStaff storeStaff = new StoreStaff();
        //将门店id和员工id设置到该关联对象
        storeStaff.setStaffId(staffId);
        storeStaff.setStoreId(storeId);
        storeStaffMapper.insert(storeStaff);
    }

    //添加两个方法 作为员工的在同一个商户的账号和手机号
    Boolean isExistStaffByMobile(String mobile,Long merchantId){
        //根据商户id和手机号联合查询staff员工表的员工记录唯一性，没有查询到，可以用改手机号作为该员工的新增员工 表
        Integer count = staffMapper.selectCount(new LambdaQueryWrapper<Staff>().eq(Staff::getMerchantId, merchantId)
                .eq(Staff::getMobile, mobile));
        return count >0;

    }

    //用一个商户下员工的账号唯一性
    Boolean isExistStaffByUsername(String username,Long merchantId){
        //根据商户id和员工的账号进行联合查询staff表，没有记录，该账号可以作为该员工的账号新增到staff表中
        Integer count = staffMapper.selectCount(new LambdaQueryWrapper<Staff>()
                .eq(Staff::getUsername, username)
                .eq(Staff::getMerchantId, merchantId));
        return count > 0;
    }


}
